package org.mrjbee.share.it.easy.client.daemon.http;

import java.awt.Desktop;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;

import org.mrjbee.share.it.easy.client.daemon.ConfigurationProvider;
import org.mrjbee.share.it.easy.client.daemon.Log;

import com.google.api.client.auth.oauth.OAuthAuthorizeTemporaryTokenUrl;
import com.google.api.client.auth.oauth.OAuthCredentialsResponse;
import com.google.api.client.auth.oauth.OAuthGetAccessToken;
import com.google.api.client.auth.oauth.OAuthGetTemporaryToken;
import com.google.api.client.auth.oauth.OAuthHmacSigner;
import com.google.api.client.auth.oauth.OAuthParameters;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;

public class GAEHttpManager implements HttpManager {
	 
	private static final HttpTransport TRANSPORT = new NetHttpTransport();
	
	private static final String CONSUMER_KEY = "anonymous";
	private static final String CONSUMER_SECRET = "anonymous";
	
	private static final String REQUEST_TOKEN_URL = "/_ah/OAuthGetRequestToken";
	private static final String AUTHORIZE_URL = "/_ah/OAuthAuthorizeToken";
	private static final String ACCESS_TOKEN_URL = "/_ah/OAuthGetAccessToken";
	private static final String DATA_URL = "/data";
	
	private String serverUrl;
	private OAuthParameters requestParameters;
	
	
	/* (non-Javadoc)
	 * @see org.mrjbee.shared.clipboard.client.daemon.http.HttpManager#initClient(org.mrjbee.shared.clipboard.client.daemon.ConfigurationProvider)
	 */
	public void initClient(ConfigurationProvider conf) throws Exception {
		serverUrl = conf.getServerBaseUrl();
		String[] tokenInfo = null;
		if(!conf.isTokenExists()){
			Log.SYS.info("Obtaining tokens started.");
			tokenInfo = askForAGrand();
			conf.storeTokenInfo(tokenInfo);
		} else {
			Log.SYS.info("Reusing existing tokens.");
			tokenInfo = new String[] {conf.getAccessToken(), conf.getTokenSecret()};
			Log.SYS.debug("Reusing token:"+tokenInfo[0]);
			Log.SYS.debug("Reusing token secret:"+tokenInfo[1]);
		}
		
		OAuthHmacSigner requestSigner = new OAuthHmacSigner();
		requestSigner.clientSharedSecret = CONSUMER_SECRET;
		requestSigner.tokenSharedSecret = tokenInfo[1];
		
		requestParameters = new OAuthParameters();
		requestParameters.consumerKey = CONSUMER_KEY;
		requestParameters.token = tokenInfo[0];
		requestParameters.signer = requestSigner;
		
		Log.SYS.info("Http manger ready.");
	}

	private String[] askForAGrand() throws Exception {
		OAuthHmacSigner signer = new OAuthHmacSigner();
		signer.clientSharedSecret = CONSUMER_SECRET;
		// Step 1: Get a request token. This is a temporary token that is used for
		// having the user authorize an access token and to sign the request to obtain
		// said access token.
		OAuthGetTemporaryToken requestToken = new OAuthGetTemporaryToken(createUrl(REQUEST_TOKEN_URL));
		requestToken.consumerKey = CONSUMER_KEY;
		requestToken.transport = TRANSPORT;
		requestToken.signer = signer;
		OAuthCredentialsResponse requestTokenResponse = requestToken.execute();
		
		Log.SYS.debug("Request Token:");
		Log.SYS.debug("    - oauth_token        = " + requestTokenResponse.token);
		Log.SYS.debug("    - oauth_token_secret = " + requestTokenResponse.tokenSecret);
		
		// updates signer's token shared secret
		signer.tokenSharedSecret = requestTokenResponse.tokenSecret;
		OAuthAuthorizeTemporaryTokenUrl authorizeUrl = new OAuthAuthorizeTemporaryTokenUrl(createUrl(AUTHORIZE_URL));
		authorizeUrl.temporaryToken = requestTokenResponse.token; 
		String autUrl = authorizeUrl.build();
		if (Desktop.isDesktopSupported()){
			try {
				Desktop.getDesktop().browse(new URI(autUrl));
			} catch (Exception e) {
				Log.SYS.error("Error during opening browser",e);
				throw e;
			}
			Log.SYS.debug("Thread going to sleep for 20sec allowing user to prove grand.");
			Thread.sleep(20000);
			Log.SYS.debug("Thread going to sleep for 20sec");
		} else {
			String currentLine = "n";
			System.out.println("Go to the following link in your browser:\n"+autUrl);
			InputStreamReader converter = new InputStreamReader(System.in);
			BufferedReader in = new BufferedReader(converter);
			while (currentLine.equalsIgnoreCase("n")) {
			   System.out.println("Have you authorized me? (y/n)");
			   currentLine = in.readLine();
			}
		}
		OAuthCredentialsResponse accessTokenResponse = null;
		// Step 3: Once the consumer has redirected the user back to the oauth_callback
		// URL you can request the access token the user has approved. You use the
		// request token to sign this request. After this is done you throw away the
		// request token and use the access token returned. You should store this
		// access token somewhere safe, like a database, for future use.
		OAuthGetAccessToken accessToken = new OAuthGetAccessToken(createUrl(ACCESS_TOKEN_URL));
		accessToken.consumerKey = CONSUMER_KEY;
		accessToken.signer = signer;
		accessToken.transport = TRANSPORT;
		accessToken.temporaryToken = requestTokenResponse.token;
		
		try {
		    accessTokenResponse = accessToken.execute();
		} catch (Exception e) {
			accessTokenResponse = null;
			e.printStackTrace();
		} 
		
		if (accessTokenResponse == null){
			throw new Exception("Not authorized.");
		}
		
		Log.SYS.debug("Access Token:");
		Log.SYS.debug("    - oauth_token        = " + accessTokenResponse.token);
		Log.SYS.debug("    - oauth_token_secret = " + accessTokenResponse.tokenSecret);
		Log.SYS.debug("\nYou may now access protected resources using the access tokens above.");
		String[] answer = {accessTokenResponse.token,accessTokenResponse.tokenSecret};
		return answer;
	}

	private String createUrl(String urlPostfix) {
		return serverUrl+urlPostfix;
	}

	/* (non-Javadoc)
	 * @see org.mrjbee.shared.clipboard.client.daemon.http.HttpManager#dropClient()
	 */
	public void dropClient() {
	
	}

	/* (non-Javadoc)
	 * @see org.mrjbee.shared.clipboard.client.daemon.http.HttpManager#publishString(java.lang.String)
	 */
	public boolean publishString(String string) {
		// utilize accessToken to access protected resources
		HttpRequestFactory factory = TRANSPORT.createRequestFactory(requestParameters);
		GenericUrl url = new GenericUrl(createUrl(DATA_URL));
		HttpRequest req = null;
		HttpResponse resp = null;
		try {
			req = factory.buildPostRequest(url, ByteArrayContent.fromString("text/plain", string));
			resp = req.execute();
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

		if (resp.isSuccessStatusCode()) {
			try {
				return true;
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		} else {
			Log.SYS.debug("Response Status:" + resp.getStatusCode() + "["
					+ resp.getStatusMessage() + "]");
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see org.mrjbee.shared.clipboard.client.daemon.http.HttpManager#grabString()
	 */
	public String grabString() {
		// utilize accessToken to access protected resources
		HttpRequestFactory factory = TRANSPORT
				.createRequestFactory(requestParameters);
		GenericUrl url = new GenericUrl(createUrl(DATA_URL));
		HttpRequest req = null;
		HttpResponse resp = null;
		try {
			req = factory.buildGetRequest(url);
			resp = req.execute();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}

		if (resp.isSuccessStatusCode()) {
			try {
				return resp.parseAsString();
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		} else {
			Log.SYS.debug("Response Status:" + resp.getStatusCode() + "["
					+ resp.getStatusMessage() + "]");
			return null;
		}
	}

}
